package com.xingu.xg.service.impl;


import com.xingu.xg.common.consts.ComRT;
import com.xingu.xg.common.consts.ComResult;
import com.xingu.xg.dal.domain.Category1;
import com.xingu.xg.dal.dto.Category1TreeNode;
import com.xingu.xg.dal.vo.Category1FormVo;
import com.xingu.xg.dal.vo.category.req.CopyNodeQuery;
import com.xingu.xg.mapper.Category1Mapper;
import com.xingu.xg.service.Category1Service;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

//@Slf4j
@Service
public class Category1ServiceImpl implements Category1Service {

    Category1Mapper categoryMapper;

    PlatformTransactionManager transactionManager;

    @Autowired
    public Category1ServiceImpl(Category1Mapper categoryMapper,PlatformTransactionManager transactionManager){
        this.categoryMapper = categoryMapper;
        this.transactionManager = transactionManager;
    }




    private static Logger logger = LogManager.getLogger(Category1ServiceImpl.class);

    @Override
    public ComResult<Category1> create(Category1FormVo req) {

        // 新增逻辑
        Category1 data = new Category1();
        data.setDeleted(false);
        data.setSort(0);
        BeanUtils.copyProperties( req , data);

        int insert = categoryMapper.insert(data);

        if(insert <= 0){
            return new ComResult(500,"新增失败",req);
        }

        return ComRT.success(data);
    }



    @Override
    public ComResult<Category1> deleteBatch(List<Long> ids) {
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);


        try{
            for (Long id : ids) {
                categoryMapper.deleteById(id);
                // 级联删除
                List<Category1> allChildren = getChildCategoryList(id);

                List<Long> childIds = allChildren.stream().map(Category1::getId).collect(Collectors.toList());
                categoryMapper.deleteBatchIds(childIds);
            }

            transactionManager.commit(transactionStatus);
        }catch (Exception e){
            logger.log(Level.forName("xg", 150), new Date() + e.getMessage());
            transactionManager.rollback(transactionStatus);
            return ComRT.fail(500,"当前删除失败请联系管理员");
        }

        return ComRT.success();
    }



    @Override
    public List<Category1TreeNode> selectTree( ) {

        List<Category1> source = categoryMapper.selectList(null);

        List<Category1TreeNode> level0RootArray = buildTree(source);

        return level0RootArray;
    }

    @Override
    public List<Category1TreeNode> buildTree(List<Category1> source) {

        // 获取所有相关的节点
        // 排序
        source.sort( Comparator.comparing( Category1::getSort ) );
        // 树形缓存
        Map<Long , Category1TreeNode> treeNodeMap = new LinkedHashMap<>();
        source.forEach( category -> treeNodeMap.put(category.getId(), convert(category) ) );

        // 直接遍历每一个节点 将这个节点添加到父级的children的关联中
        treeNodeMap.values().stream().filter( ite -> !ite.getLevel().equals(0) ).forEach( childNode -> {
            // 获取父 node 将自己添加到children的list下
            Category1TreeNode parentNode = treeNodeMap.get(childNode.getPid());
            parentNode.getChildren().add( childNode );
        } );

        List<Category1TreeNode> res = treeNodeMap.values().stream().filter(ite -> ite.getLevel().equals(0)).collect(Collectors.toList());


        return res;
    }

    @Override
    public List<Category1> getChildCategoryList(Long id) {

        LinkedList<Category1> res = new LinkedList<>();
        Collection<Long> parentIds = Collections.singleton(id);

        for (int i=0; i<Short.MAX_VALUE ;i++){
            // 查询当前层所有的子分类
            List<Category1> category1s = categoryMapper.selectByParentId(parentIds);

            if(CollectionUtils.isEmpty(category1s)){
                break;
            }

            res.addAll(category1s);
            // 如果有下一层则通过 pid数组查询出列表
            parentIds = category1s.stream().map(Category1::getId).filter(Objects::nonNull).collect(Collectors.toSet());
        }

        return res;
    }

    @Override
    public ComResult<Category1> copyNodeFromId(CopyNodeQuery req) {
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);


        try{
            Long oriNodeId = Long.valueOf(req.getOriginalId());
            Long targetParentId = Long.valueOf(req.getOriginalParentId());

            Category1 parentNode = categoryMapper.selectById(targetParentId);
            // 获取当前选择节点下的所有的树结构
            Category1TreeNode root = getCategoryTree(oriNodeId);
            // 层次遍历这个树 同时在新的树中同步这个子树的结构
            Queue<Category1TreeNode> queue = new LinkedList<>();
            queue.offer( root );

            // 更新子节点的状态
            root.setPid( parentNode.getId() );
            root.setLevel(parentNode.getLevel()+1 );


            while( !queue.isEmpty() ){
                int len = queue.size();

                while( len>0 ){
                    Category1TreeNode curLevelNode = queue.poll();
                    Category1 curLevelNodeData = convert(curLevelNode);

                    curLevelNodeData.setId(null);
                    categoryMapper.insert(curLevelNodeData);


                    curLevelNode.getChildren().forEach(node->{
                        node.setPid(curLevelNodeData.getId() );
                        node.setLevel(curLevelNodeData.getLevel()+1 );
                    });
                    // 放入下一层节点
                    queue.addAll( curLevelNode.getChildren());
                    len--;
                }

            }

            transactionManager.commit(transactionStatus);
        }catch (Exception e){
            e.printStackTrace();
            logger.log(Level.forName("xg", 150), new Date() + e.getMessage());
            transactionManager.rollback(transactionStatus);
            return ComRT.fail(500,"复制节点失败");
        }

        return ComRT.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ComResult createSameLevel(Long curNodeId) {
        Category1 node = categoryMapper.selectById(curNodeId);


        Category1 beforeNode = new Category1();
        beforeNode.setPid(node.getPid());
        beforeNode.setLevel(node.getLevel());
        beforeNode.setName("same level but sort+1");
        beforeNode.setSort((node.getSort()==null?0:node.getSort())+1);
        Category1 afterNode = new Category1();
        afterNode.setPid(node.getPid());
        afterNode.setLevel(node.getLevel());
        afterNode.setSort((node.getSort()==null?0:node.getSort())-1);
        afterNode.setName("same level but sort-1");

        categoryMapper.insert(beforeNode);
        categoryMapper.insert(afterNode);
        return ComRT.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ComResult createFatherAndSub(Long curNodeId) {

        Category1 node = categoryMapper.selectById(curNodeId);


        Category1 fatherNode = new Category1();
        if(node.getLevel() != 0){
            List<Category1> category1s = categoryMapper.selectByParentId( node.getPid() );

            if(category1s.size()!=0){
                fatherNode.setLevel( node.getLevel() - 1 );
                if( fatherNode.getLevel() == 0 ){
                    fatherNode.setPid(0l);
                }else{
                    fatherNode.setPid(category1s.get(0).getPid());
                }
                fatherNode.setSort(0);
                fatherNode.setName("auto create father node");
                categoryMapper.insert(fatherNode);
            }
        }

        Category1 subNode = new Category1();
        subNode.setPid( node.getId() );
        subNode.setLevel(node.getLevel()+1);
        subNode.setSort(0);
        subNode.setName("auto create sub node");
        categoryMapper.insert(subNode);

        return ComRT.success();
    }


    private Category1TreeNode getCategoryTree( Long id ){

        // 获取所有相关的节点
        Category1 rootData = categoryMapper.selectById(id);
        List<Category1> childCategoryList = getChildCategoryList(id);
        // 排序
        childCategoryList.sort( Comparator.comparing( Category1::getSort ) );

        // 树形缓存
        Map<Long , Category1TreeNode> treeNodeMap = new LinkedHashMap<>();
        treeNodeMap.put( rootData.getId() , convert(rootData) );
        childCategoryList.forEach( category -> treeNodeMap.put(category.getId(), convert(category) ) );


        // 直接遍历每一个节点 将这个节点添加到父级的children的关联中
        Category1TreeNode root = treeNodeMap.get(id);
        treeNodeMap.values().stream().filter( ite -> !ite.getId().equals(id) ).forEach( childNode -> {
            // 获取父 node 将自己添加到children的list下
            Category1TreeNode parentNode = treeNodeMap.get(childNode.getPid());
            parentNode.getChildren().add( childNode );
        } );

        return root;
    }




    public Category1TreeNode convert( Category1 source ){
        Category1TreeNode data = new Category1TreeNode();
        BeanUtils.copyProperties( source,data );
        return data;
    }

    public Category1 convert( Category1TreeNode source ){
        Category1 data = new Category1();
        BeanUtils.copyProperties( source,data );
        return data;
    }

}
